home *** CD-ROM | disk | FTP | other *** search
/ Amiga News 96 / Amiga News 96.iso / amig_ad_os / laurent_faillie / lfcinter / lfcinter.txt < prev    next >
Text File  |  1977-12-31  |  27KB  |  855 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.                             L F C I n t e r
  7.  
  8.                         Un mini interpréteur C.
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26.                         Par Laurent FAILLIE
  27.  
  28.                 Pour tout renseignement écrire à
  29.                          Laurent FAILLIE
  30.                           "Les VUARDES"
  31.                         F-74930 PERS-JUSSY
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.                                     1
  59.                                Présentation
  60.  
  61. But:
  62.      LFCInter est un interpréteur de langage C (réduit).
  63.  
  64.      Ce programme est un FREEWARE: Il peut être diffusé librement temps que
  65. tous les fichiers de cette distribution sont inclus, sans que mon copyright
  66. n'ai été modifié. Toutes les modifications faites pour supprimer un buggue
  67. ou pour porter ce code vers une autre plateforme DOIVENT ETRE CLAIREMENT
  68. IDENTIFIEES DANS LE CODE. De plus, je vous serais reconnaissant de m'avertir
  69. de telles modifications...
  70.  
  71.      Voici donc le résultat de 6 mois de programmation, pendant mes heures de
  72. loisir...
  73.  
  74. Le développement:
  75.     Le langage C++ a été choisi. "L'approche objet" a été utilisée lorsqu'elle
  76. simplifiait la programmation comme pour la gestion des mots clefs ou des
  77. erreurs. Une programmation plus classique a été utilisée lors de certaines
  78. tâches afin de diminuer le code: Par exemple, les opérateurs des calculs,
  79. comme '+', '-', ... auraient pu être des opérateurs surchargés pour chaque
  80. classe de représentation des valeurs mais le code nécessaire était trop
  81. important!
  82.     Afin de garantir la portabilité sur plusieurs plate-formes, GCC a été
  83. utilisé (version 2.7.0 disponible sur le CD Fresh Fish 10 pour Amiga). Ce
  84. compilateur du 'Domain Public', donc gratuit, est disponible sur toutes les
  85. machines, du simple PC jusqu'aux plus puissantes mainframes. En plus de suivre
  86. correctement la norme, le code généré comporte peu de buggues...
  87.  
  88.      Le développement a été fait sur:
  89.     - Amiga 4000 (68040, 14Mo de RAM, DD 120 Mo, CD-ROM x4, WB et KS 3.0)
  90.     - Amiga 1000 (68010, 8Mo de RAM, DD 52 Mo, WB et KS 2.1)
  91.     - MicroVaxII (16 Mo de RAM, deux RD54, sous ultrix 4.2 avec GCC compilé à
  92. partir des sources incluses dans le CD de la version Amiga).
  93.  
  94. Portabilité:
  95.      LFCInter a principalement été écrit pour les systèmes où les entiers et
  96. les pointeurs ont la même taille (indispensable lors de convertions entier <->
  97. pointeur mais aussi nécessaire aux opérateurs de comparaison)... C'est le cas
  98. de tous les systèmes 32 bits, certains 64 bits. Comme d'habitude, le problème
  99. se pose sur PC où certains modes de compilation autorisent des entiers sur 16
  100. bits et des pointeurs sur 32 bits.
  101.     L'utilisation de l'option '-fatal' permet un test en temps réèl de tels
  102. débordements et provoque une erreur en cas de perte de précision.
  103.     MICRO-SUCKER est encore passé par là !!
  104.  
  105.    La compilation sur le MicroVaxII m'a permis de tester la portabilité vers
  106. les plate-formes UNIX (aucun problème supplémentaire sur le code par rapport
  107. à la compilation sur Amiga, sauf pour quelques fonctions comme expliqué plus
  108. loin). Les environnements ciblés lors du développement ont toujours été
  109. l'AmigaDOS et UNIX. La compilation sous MS-DOS par le BORLAND C++ 4.52 n'a
  110. été nécéssaire que pour la démonstration sur un portable PC. Les modifications
  111. nécessaires ont été faites uniquement lorsqu'elles n'entraînaient pas des
  112. incompatibilités avec les autres environnements, sinon les portions de codes
  113. ont été supprimés (#ifndef __BCPLUSPLUS__). De plus, n'ayant pas de machine
  114. sous cet OS incipide et anachronique chez moi, il est évident que cette
  115. version a été moins testée et comporte, sans doute, plus de buggues que les
  116. autres. Sur les systèmes plus ou moins apparentés a UNIX, en utilisant GCC,
  117. le seul problème potentiel est inexistence de certaines fonctions (par exemple
  118. isiso() sur le VAX) ou encore des noms différents pour une même fonction
  119. (stricmp() qui devient strcasecmp() pour les compilateurs 'POSIX compliant').
  120.  
  121.  
  122.  
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.  
  132.  
  133.  
  134.  
  135.  
  136.  
  137.  
  138.  
  139.  
  140.  
  141.                                     2
  142.  
  143.                 Structures et classes importantes
  144.  
  145.  
  146.     class _token {
  147.     public:
  148.         _token(const char *x);
  149.         _token();
  150.  
  151.         int operator * ();
  152.         operator const char * ();
  153.         _token operator ++ ();
  154.         _token operator ++ (int);
  155.         void saute(const char *);
  156.         string obj();
  157.  
  158.         bool definition();
  159.         bool interne();
  160.  
  161.         int hash;
  162.  
  163.     protected:
  164.         const char *ptr;
  165.         short int val;
  166.         size_t len;
  167.  
  168.     private:
  169.         void construit( bool );
  170.     };
  171.  
  172.      Cette classe, dont les instances peuvent être utilisées comme on le
  173. ferait avec des pointeurs, implémente en elle-même le 'parser'. Son champ
  174. principal
  175.     const char *ptr;
  176. pointe sur le premier caractère de l'objet en cours d'évaluation. Il est
  177. 'protected' et, en aucun cas, il ne doit être modifié directement car lui sont
  178. associés les champs
  179.     size_t len;
  180. qui contient la longueur de l'objet, et
  181.     short int val;
  182. qui contient la 'valeur symbolique' correspondante. Cette valeur entière,
  183. définie sous forme d'énumération dans le fichier "Token.h", accélère les tests
  184. de syntaxe par la suite. On distingue les valeurs 'smbl_id' qui indique que
  185. l'objet est un identificateur et 'smbl_icn' pour signifier la fin du fichier
  186. source ou que l'instance n'est pas initialisée. A chaque modification de 'ptr'
  187. ces champs sont mis-à jour par la méthode construit(). Dans cette classe, se
  188. trouve aussi
  189.     l'opérateur * qui renvoit la valeur symbolique associée à l'objet courant,
  190.     la méthode obj() qui retourne une 'string' contenant l'objet pointé,
  191. idéale pour afficher les messages d'erreurs,
  192.     les opérateurs ++ qui 'incrémentent' ptr pour qu'il pointe sur l'objet
  193. significatif suivant; par significatif, s'entend que les espaces et les
  194. commentaires sont sautés,
  195.     la méthode saute() qui permet de ... sauter, une instruction, un bloc
  196. d'instructions, à la fin d'un () ou d'un [].
  197.     Les méthodes definition() et interne() indiquent si l'objet pointé est un
  198. mot clef de définition de type ('int', 'void', ...), ou s'il s'agit d'une
  199. fonction interne (printf(), malloc(), free()),
  200.  
  201.     class _rep {
  202.     public:
  203.         _repval val;
  204.         const char type() const;
  205.  
  206.         _rep();
  207.         _rep(const int , const char );
  208.         _rep(const long );
  209.         _rep(const char );
  210.         _rep(void *, const char, const string vers);
  211.  
  212.         string info;
  213.  
  214.         bool nonnull(const char *);
  215.     protected:
  216.         char typebase;
  217.     };
  218.  
  219.      Cette classe de "représentation de valeurs", contient, en plus des
  220. valeurs en elles-mêmes, son type. Le 'type de base', c'est à dire celui qui
  221. caractérise comment la valeur est mémorisée, est accessible par la méthode
  222. type() et peut prendre les valeurs suivantes
  223.     - '*' : c'est un pointeur,
  224.     - 'I' : c'est un entier,
  225.     - 'C' : c'est un caractère,
  226.     - 'V' : c'est une valeur 'void',
  227.     - 'L' : c'est une valeur littérale, c'est à dire le type permettant de
  228. stocker n'importe quelle autre valeur. Il est défini comme un 'long int'.
  229. Pour un pointeur, le champ
  230.     string info;
  231. contient le type de ce vers quoi il pointe. Par exemple, pour un pointeur
  232. sur des caractères (une chaîne ?), typebase vaut '*' et info vaut "C".
  233.  
  234.     struct _var {
  235.         _var(_var *, const string &, const string &, const int);
  236.         ~_var();
  237.  
  238.         virtual void operator = (_rep )=0;
  239.         virtual _rep repval(const char *)=0; // Retourne la valeur stockée
  240.         virtual void *refval()=0; // Retourne une reférence sur cette valeur
  241.     protected:
  242.         void veriftype(_rep &);
  243.         _var *succ;
  244.  
  245.     public:
  246.         string nom;     // Nom du symbole
  247.         string type;    // Type de ce symbole
  248.         int h;      // Hash code pour ce symbole
  249.  
  250.         friend _var *trouve_symbole( const _var *, const string &, int);
  251.     };
  252.  
  253.      struct _var et ces dérivées _var_int, _var_char, _var_fonc, _var_ptr
  254. permettent de mémoriser les variables. La structure de base contient les
  255. champs permettant d'identifier la variable (nom, type,...) ainsi que la
  256. définition des méthodes utilisée pour obtenir la valeur mémorisée ou une
  257. référence sur celle-ci. Chaque classe dérivée contient un champ 'val' qui est
  258. l'espace de stockage pour le contenu de la variable. Le champ 'succ' sert à
  259. lier les symboles ensemble (voir la structure _tablesmb) et 'h' contient le
  260. hash code du nom pour accélérer les recherches.
  261.  
  262.     class _resallouee {
  263.     void *data;
  264.     _resallouee *succ;
  265.  
  266.      public:
  267.     _resallouee( _resallouee *, void * );
  268.     ~_resallouee();
  269.  
  270.     friend _var *ajoute_symbole( _var **, const string &,
  271.             const string &,  const char *, void *, int);
  272.     };
  273.  
  274.      Cette classe mémorise les différents blocs de mémoire associés à un
  275. tableau. Ainsi, ils seront libérés lors de sa destruction. Cette classe ne
  276. comporte que 2 champs
  277.     void *data;
  278. pointeur sur le bloc de données, alloué avec malloc(),
  279.     _resallouee *succ;
  280. pointeur sur la ressource suivante. En effet, pour un tableau
  281.     char x[5][10];
  282. 6 ressources ont été allouées, 5 contenant 10 caractères pour stocker les
  283. données de la seconde dimension, plus 1 contenant 5 pointeurs pour stocker la
  284. premiere (des tableaux de tableaux sont en fait des tableaux de pointeurs sur
  285. les premiers éléments des dimentions inférieures !). Grâce à ce système, des
  286. tableaux de dimension quelconque peuvent être alloués... dans la limite de la
  287. mémoire disponible.
  288.  
  289.     struct _tablesmb {
  290.     _tablesmb( _tablesmb * );
  291.     ~_tablesmb();
  292.  
  293.     _var *var;      // premier symbole de la liste
  294.     _tablesmb *parent;  // Tds précedente
  295.     };
  296.  
  297.      Plutôt que d'utiliser des tables de symboles statiques comme le font la
  298. plus part des compilateurs ou des interpréteurs, j'utilise des tables
  299. dynamiques contituées d'une liste simplement chaînée, gérée par cette
  300. structure. La fonction trouve_symbole() permet de trouver un symbole dans la
  301. table courante, dans celles des blocs parents ou enfin dans la table des
  302. symboles globaux.
  303.  
  304.      template <class T> class LFDynaStack {
  305.      private:
  306.       /*
  307.        *  Segments de données
  308.        */
  309.       struct LFDSData {
  310.           LFDSData *suivant, *precedent;
  311.           T *data;
  312.       } *premier, *dernier, *courant;
  313.  
  314.       /*
  315.        * Index
  316.        */
  317.       int nbreparseg; // Nombre d'objets par segment
  318.       int maxidx;     // Nombre d'objets dans la pile
  319.       int cidx;       // Idx de l'objet courrant
  320.  
  321.      public:
  322.       LFDynaStack(int);
  323.       ~LFDynaStack();
  324.  
  325.       bool Push(T);
  326.       T Pop();
  327.  
  328.       T &operator[](int);
  329.  
  330.       int length();
  331.       int current();
  332.       };
  333.  
  334.      Cette classe implémente une pile de données homogènes. Au lieu d'allouer
  335. les données une par une comme il faudrait le faire avec une liste doublement
  336. chaînée, ce qui a tendance à fragmenter la mémoire (et à faire ramer les PC !),
  337. elles sont regroupées en blocs. Le nombre d'allocation/libération étant
  338. diminué, l'exécution des fonctions utilisant des piles, comme lors de calcul,
  339. est accélérée...
  340.  Les blocs sont chaînés entre eux (structure LFDSData). Le champ 'cidx'
  341. mémorise quelle est la dernière donnée accédée et 'courant' dans quel bloc
  342. elle se trouve. Comme la majorité des accès à ce genre de pile est séquenciel,
  343. ces champs évitent de nombreuses recherches de blocs.
  344.  Note: Lorsque l'on tente d'accéder à une donnée qui n'existe pas (index hors
  345. limite), une donnée créée par le constructeur par défaut est renvoyé. Cette
  346. classe dispose des méthodes et des opérateurs suivants:
  347.     Push() : Ajoute une donnée au sommet de la pile, en créant s'il le faut un
  348. nouveau bloc,
  349.     Pop() : Retourne la dernière donnée poussée et la supprime de la pile,
  350.     operator[] : Retourne une référence sur la donnée de la pile dont l'index
  351. est passé en argument,
  352.     current() et length(): Renvoient respectivement l'index de l'élément
  353. courant et du dernier.
  354.  
  355.  
  356.  
  357.  
  358.  
  359.  
  360.  
  361.  
  362.  
  363.  
  364.  
  365.  
  366.  
  367.  
  368.  
  369.  
  370.  
  371.                                         3
  372.  
  373.                         Comment est interprété le source?
  374.  
  375.  
  376.     Voici les différentes étapes effectuées lors du lancement  d'LFCInter:
  377.  
  378. 1/ Décodage et mémorisation des arguments de l'interpréteur,
  379.  
  380. 2/ Lecture et stockage dans le buffer du fichier à interpréter,
  381.  
  382. 3/ Parcours rapide du buffer à la recherche des symboles globaux qui sont
  383. stockés dans la table globale,
  384.  
  385. 4/ Construction des paramêtres de la fonction main(), argc et argv grâce aux
  386. arguments restant dans la ligne de commande,
  387.  
  388. 5/ interprétation de la fonction main(), qui provoquera l'interprétation des
  389. fonctions qu'elle appelle...
  390.  
  391.  
  392.     L'interprétation est elle-même est assurée par les fonctions:
  393.  
  394.     execfonc() a pour tâche principale de décoder les arguments d'une fonction
  395. et de tester si sa valeur de retour est correcte (par exemple, pas de valeur de
  396. retour s'il s'agit d'une fonction déclarée comme 'void'). Elle lance ensuite
  397. execbloc() sur le bloc principale de la fonction,
  398.  
  399.     execbloc() interprète un bloc. Si le premier élément lu est un '{',
  400. l'interprétation se terminera au '}' correspondant, dans le cas contraire une
  401. seule instruction est interprétée en lançant la fonction execinst(),
  402.  
  403.     execinst() exécute une seule instruction. Pour celles qui sont répétitives,
  404. une sorte de cache est utilisé afin de ne tester leur syntaxe qu'une seule
  405. fois. S'il ne s'agit pas d'un mot clef du C, il s'agit d'un calcul donc eval()
  406. est appellée (voir le § des calculs). Si un '{' est rencontré, execbloc() est
  407. appelée,
  408.  
  409.     Si dans l'expression a calculer se trouve une fonction utilisateur,
  410. lancefonc() est appelée: Elle lira les arguments et lancera execfonc(),
  411.  
  412.     Si une fonction interne est détectée, interne() est appelée: Elle contient
  413. l'interface entre le programme source interprété et les fonctions du
  414. compilateur. Dans la magorité des cas, il ne s'agit que de tester la validité
  415. des arguments mais pour les fonction plus complexes comme le printf(),le code
  416. nécessaire est plus important.
  417.  
  418.  
  419.  
  420.  
  421.  
  422.  
  423.  
  424.  
  425.  
  426.  
  427.  
  428.  
  429.  
  430.  
  431.  
  432.  
  433.  
  434.                                         4
  435.  
  436.                                     Les calculs
  437.  
  438.  
  439.      Cette B.N.F. est une adaptation de celle fournie dans la documentation du
  440. BORLAND C++ 4.0 (entre parenthèses se trouve la priorité de chaque 'couche'.)
  441.  
  442.   (-11) expression ::= exp_affect {[, exp_affect ]}
  443.     Le résultat de l'expression est la valeur de la derniere exp_affect.
  444.  
  445.   (-10) exp_affect ::= exp_un opp_aff exp_affect
  446.            ::= expr_cond
  447.  
  448.     avec opp_aff '=','*=','/=','%=','+=','-=','&=','^=','|=','<<=','>>='
  449.      exp_un doit être une référence.
  450.  
  451.   (-9)  expr_cond ::= exp_OU_log ? exp_OU_log : expr_cond
  452.            ::= exp_OU_log
  453.  
  454.   (-8)  exp_OU_log ::= exp_OU_log || exp_ET_log
  455.         ::= exp_ET_log
  456.  
  457.   (-7)  exp_ET_log ::= exp_ET_log && exp_OU_incl
  458.            ::= exp_OU_incl
  459.  
  460.   (-6)  exp_OU_incl ::= exp_OU_incl | exp_OU_excl
  461.             ::= exp_OU_excl
  462.  
  463.   (-5)  exp_OU_excl ::= exp_OU_excl ^ exp_ET
  464.             ::= exp_ET
  465.  
  466.   (-4)  exp_ET ::= exp_ET & exp_egal
  467.            ::= exp_egal
  468.  
  469.   (-3)  exp_egal ::= exp_egal == exp_rel
  470.          ::= exp_egal != exp_rel
  471.          ::= exp_rel
  472.  
  473.   (-2)  exp_rel ::= exp_rel opp_rel exp_dcl
  474.         ::= exp_dcl
  475.  
  476.     avec opp_rel '<','>','<=','>='
  477.  
  478.   (-1)  exp_dcl ::= exp_dcl >> exp_add
  479.         ::= exp_dcl << exp_add
  480.         ::= exp_add
  481.  
  482.   (0)   exp_add ::= exp_add + exp_mul
  483.         ::= exp_add - exp_mul
  484.         ::= exp_mul
  485.  
  486.   (1)   exp_mul ::= exp_mul   exp_un
  487.         ::= exp_mul / exp_un
  488.         ::= exp_mul % exp_un
  489.         ::= exp_un
  490.  
  491.   (2)   exp_un ::= opp_un exp_un
  492.            ::=  exp_posf
  493.  
  494.     avec opp_un '++','--','&','*','+','-','~','!'
  495.  
  496.   (3)   exp_posf ::= exp_prim
  497.          ::= exp_posf++
  498.          ::= exp_posf--
  499.          ::= exp_posf [expr_cond]{[expr_cond]}
  500.                         Valeur d'un tableau
  501.  
  502.   (4)   exp_prim ::= valeur_littéral
  503.          ::= variable
  504.          ::= fonction (...)     Appel de fonction
  505.          ::= ( exp_affect )
  506.  
  507.      L'entête du fichier LFCI_Cal.cxx contient plus d'informations sur les
  508. différences entre cette BNF et celle du BORLAND (généralement des erreurs dans
  509. la BNF incluse dans la documentation de ce compilateur !).
  510.  
  511.      Si cette BNF permet de décrire formellement le langage, la transposer
  512. directement en code C est totalement inadapté à un interpréteur. Par exemple,
  513. prenons le cas de la couche de niveau 0:
  514.       (0)   exp_add ::= exp_add + exp_mul
  515.             ::= exp_add - exp_mul
  516.             ::= exp_mul
  517.      Si l'on suit la BNF "à la lettre", le programme doit rechercher le DERNIER
  518. signe '+' ou '-' pour séparer 'exp_add' et 'exp_mul'. Dans le cas de
  519. l'expression "2+5*-3", il trouvera d'abors le '-' et devra déterminer s'il
  520. s'agit bien du '-' binaire ou du '-' unaire. Pour ce faire, il doit avoir
  521. 'parsé' tout le début de l'expression! Dans ce cas c'est non, donc le programme
  522. recherche le symbole précédent et trouve le '+', et à nouveau, il doit à
  523. nouveau chercher si cet opérateur est binaire. En plus, s'il n'y a aucune
  524. optimisation, il reparsera une nouvelle fois l'expression depuis le début, et
  525. la même chose se passera pour toute les couches... Quelle perte de temps!
  526. Plutot que d'utiliser ce genre de code, j'ai préféré utiliser un 'remix' entre
  527. une BNF restreinte et du code utilisant la méthode des "priorités des
  528. opérateurs". Voici donc cette BNF restreinte, codée dans les fichiers
  529. LFCI_Cal.cxx et LFCI_icl.cxx:
  530.  
  531.     reponse ::= exp_aff {, exp_aff}
  532.     codé dans la fonction eval().
  533.  
  534.     exp_aff ::= reférence {oppaff exp_cond}
  535.     exp_aff ::= exp_cond
  536.     codé dans la fonction affectation().
  537.  
  538.     exp_cond ::= exp_binaire ? exp_cond : exp_cond
  539.     exp_cond ::= exp_binaire
  540.     codé dans la fonction conditionnel().
  541.  
  542.     exp_binaire ::= exp_un {[opérateur_binaire exp_un]}
  543.     codé dans la fonction binaire().
  544.  
  545.     exp_un ::= [{pré-opérateur}] exp_posf
  546.     codé dans la fonction lectunaire().
  547.  
  548.     exp_posf ::= exp_prim [{post-opérateur}]
  549.     codé dans la fonction lectunaire().
  550.  
  551.     exp_prim ::= valeur_littéral
  552.          ::= variable
  553.          ::= fonction (...)     Appel de fonction
  554.          ::= ( exp_affect )
  555.     codé dans la fonction CalcLexer().
  556.  
  557.  
  558.  
  559.  
  560.  
  561.  
  562.  
  563.  
  564.  
  565.  
  566.  
  567.  
  568.  
  569.  
  570.  
  571.  
  572.  
  573.  
  574.                                         5
  575.  
  576.                     Les limitations de la version actuelle
  577.  
  578.  
  579.   Il est évident qu'un tel projet est complexe et demande beaucoup de temps.
  580. C'est pourquoi, cet interpréteur comporte certaines limitations par rapport au
  581. C standard:
  582.  
  583.     - Pas d'instructions du préprocesseur (ce qui est normal pour un langage
  584. interprété !), il est cependant assez simple d'ajouter un mécanisme de #define
  585. pour les constantes,
  586.  
  587.     - Pas de prototype, ce qui est d'ailleur inutile car, dans un premier
  588. temps, LFCInter construit une table des symboles globale, où sont incluses les
  589. fonctions,
  590.  
  591.     - il est impossible d'utiliser des fonctions qui demandent un nombre
  592. variable d'arguments (manque de temps pour implémenter des 'va_list' qui soient
  593. compatibles avec les autres compilateurs). La seule exception concerne les
  594. fonctions internes comme les '?printf()'.
  595.  
  596.     - Certains qualificateurs sont ignorés : "register", "volatile", "short",
  597. "long", "const", "extern", "signed", "unsigned" et "auto".
  598.  
  599.     - D'autres causent des erreurs: "float", "struct", "union", "double" et
  600. "static" (voir le fichier "Token.cxx" qui contient la liste des mots clefs
  601. reconnus).
  602.  
  603.     - Pas de flottants (mais ils peuvent être facilement implémentés),
  604.  
  605.     - les identificateurs de tableaux sont manipulés en temps que pointeur. En
  606. réalité, tout au long des calculs, ils devraient concerver leur type 'T'
  607. (pointeur constant) car un code du genre
  608.         void main(){
  609.             char x[25];
  610.  
  611.             x++;
  612.         }
  613. est illégal en C. De plus, en propageant le type 'T', l'opérateur sizeof()
  614. fonctionnerait correctement sur des tableaux.
  615.  
  616.     - Pas de tableaux de taille indéterminée, comme
  617.         char ok[]={'O','u','i',0};
  618.  
  619.     - Pas de déclarations implicites,
  620.  
  621.     - Pas d'énumérations,
  622.  
  623.     - sizeof(), qui est considéré comme une fonction, n'est pas implémenté,
  624.  
  625.     - je n'ai pas eu le temps de codé l'opérateur ?:,
  626.  
  627.     - Si la fonction ne prend aucun argument, il est interdit de la définir
  628. comme
  629.         int truc( void )
  630. mais utiliser
  631.         int truc()
  632.  
  633.     - '\' ne peut être utilisé comme caractère de continuation
  634.        char *x = "Salut \
  635.        tout le monde.";
  636.    est interdit. Il fait écrire
  637.        char *x = "Salut "
  638.        "tout le monde.";
  639.    Le résultat étant le même.
  640.  
  641.     - Pas de structure, de goto, de typedef ou de switch()/case() (désolé !).
  642.  
  643.     - Par contre, contrairement à la norme ANSI, mais comme l'autorise la
  644. majorité des compilateurs, les commentaires peuvent être imbriqués.
  645.  
  646.     - Les commentaires C++ (//) sont autorisés.
  647.  
  648.     - la bibliothèques des fonctions disponibles est assez réduite : Sont
  649. implémentées la grande majorité des fonctions de chaînes (standard ANSI), les
  650. fonctions d'allocation dynamique de mémoire, de manipulation mémoire, quelques
  651. fonctions systèmes comme clock(), time() ou system(). Aucune gestion de
  652. fichiers n'est implémentée, mais la pseudo fonction (non standard)
  653. 'flushstdout()' permet de vider le tampon de sortie. L'utilisation des fichiers
  654. par les 'files descriptors' est triviale à ajouter, comme la plus part des
  655. fonctions manquantes dont le nombre d'arguments est fixe... mais je n'ai pas eu
  656. le temps. L'annexe B contient toutes les fonctions reconnues.
  657.  
  658.  
  659.  
  660.  
  661.  
  662.  
  663.  
  664.  
  665.  
  666.  
  667.  
  668.  
  669.  
  670.  
  671.  
  672.  
  673.  
  674.  
  675.  
  676.  
  677.  
  678.  
  679.  
  680.  
  681.                                         6
  682.  
  683.                                 Buggues connus
  684.  
  685.  
  686.  
  687.     - Plantage sur l'Amiga 1000 si le programme est stoppé par un CTRL-C ou par
  688. la commande Break. Je ne pense pas que ceci provienne d'LFCInter par lui-même
  689. mais d'un buggue de la 'ixemul.library' version 41.2 lors gestion des signaux
  690. avec un processeur 16/32 bits car GCC lui-même plante dans cette configuration
  691. lors d'un break. Ce problème n'existe pas sur un Amiga 1200/020 ou l'Amiga
  692. 4000/040, pleinement 32 bits...
  693.  
  694.     - Attention à certaines fonctions comme time() qui demandent un pointeur
  695. sur un entier long comme argument car sur les systèmes ayant des entiers de 16
  696. bits (PC par exemple), passer l'adresse d'un entier peut planter le système...
  697.  
  698.     - Attention, votre programme en interprété possède les mêmes buggues
  699. potentiels que s'il avait été compilé. Par exemple, à aucun moment la validité
  700. des pointeur n'est testée. De plus, comme les fonctions d'interprétation
  701. s'appellent récursivement, une pile importante peut être nécessaire...
  702.  
  703.     - Comme je l'ai indiqué plus haut, le ms-dos n'est pas la plateforme cible
  704. pour ce projet, c'est pourquoi les caractères accentués seront mal affichés
  705. sous cette... (ce n'est pas de ma faute si le borland est incapable de
  706. convertir les messages lorsque la cible est un executable DOS).
  707.  
  708.  
  709.  
  710.  
  711.  
  712.  
  713.  
  714.  
  715.  
  716.  
  717.  
  718.  
  719.  
  720.  
  721.  
  722.  
  723.  
  724.  
  725.  
  726.  
  727.  
  728.  
  729.  
  730.                                             7
  731.  
  732.                                         Conclusion
  733.  
  734.  Il est évident qu'un tel projet ne peut être parfait en si peu de temps de
  735. développement, et il doit rester encore quelques buggues. Même si LFCInter 1.0
  736. n'est pas un produit terminé, beaucoup des objectifs sont atteints:
  737.  
  738.     - Il fonctionne,
  739.     - le comportement des fichiers interprétés est très proche de celui des
  740. versions compilées,
  741.     - un nombre substanciel de fonctions a été implémenté,
  742.     - les manques importants de cette version comme les flottants ou la gestion
  743. des fichiers peuvent facilement être complétés,
  744.     - le portage sur différents environnements ne pose pas de gros problèmes...
  745.  
  746.  
  747. La réalisation de ce projet m'a aussi apporté des connaissances nouvelles:
  748.     - réalisation d'un interpréteur, et surtout de ses fonctions pour calculer
  749. une expression, qui est la partie du code la plus complexe,
  750.     - amélioration de mes connaissances dans certaines particularités du C,
  751.     - utilisation de techniques du C++ que je n'ai jamais eu le courage
  752. d'utiliser précédemment comme les 'template',
  753.     - création d'un code réellement portable (et accessoirement installation de
  754. GCC sur mon VAX).
  755.  
  756. Je tiens particulièrement à remercier BABETH et le grand LAURENT sans qui ce
  757. texte et mes sources ne seraient qu'un ramassis ignoble de fautes
  758. d'orthographes (même s'il y en reste quelques unes!). Merci pour leur patience...
  759.  
  760.  
  761.  
  762.  
  763.  
  764.  
  765.  
  766.  
  767.  
  768.  
  769.  
  770.  
  771.                                 Annexe A:
  772.                     Description des fichiers sources
  773.  
  774.  
  775.      LFCInter.h : Définitions générales utilisées dans les autres fichiers. On
  776. y trouve entre autres la définition de la structure _amsg qui mémorise les
  777. options passées en arguments et certains paramètres internes qui y sont
  778. associés...
  779.  
  780.      LFCInter.cxx : C'est ce fichier qui contient la fonction main() et quelques
  781. fonctions d'usage générale.
  782.  
  783.      LFDStack.h : Implémentation d'une classe de stockage dynamique en pile
  784. LIFO.
  785.  
  786.      Token.cxx et Token.h : Contiennent la classe qui permet de parser le texte
  787. source ainsi que l'énumération des valeurs symboliques.
  788.  
  789.      LFCI_exe.cxx : Contient le code qui permet l'exécution des blocs d'
  790. instructions, et des mots clefs du C.
  791.  
  792.      LFCI_Cal.h : Définition de classes et des prototypes utilisés lors des
  793. calculs, entre autre les représentations des valeurs (_rep), des variables
  794. (_var et ses dérivées) et le stockage des symboles (_tablesmb).
  795.  
  796.      LFCI_Cal.cxx, LFCI_icl.cxx, LFCI_icl.h : Exécution des calculs. Pour
  797. accélérer la compilation, j'ai séparé ce code en 2 fichiers. LFCI_Cal.cxx
  798. contient principalement les fonctions d'évaluation des expressions alors que
  799. LFCI_icl.cxx contient l'implémentation des calculs par eux-mêmes.
  800.  
  801.      LFCI_fnc.cxx : Contient le code des fonctions internes à LFCInter comme
  802. les printf(), malloc(), free() ...
  803.  
  804.      LFCI_Lex.cxx : Lecture des identificateurs et allocation de la mémoire
  805. pour les tableaux.
  806.  
  807.      LFCI_Var.cxx : Gestion des variables et des tables des symboles.
  808.  
  809.      LFCI_Ver.cxx : Ce fichier ne sert qu'a fournir à LFCInter sa date de
  810. compilation.
  811.  
  812.  
  813.  
  814.  
  815.  
  816.  
  817.  
  818.                                     Annexe B:
  819.                         Liste des fonctions supportées
  820.  
  821.  
  822. Note: Certaines fonctions ne sont pas disponnibles sur certaines platformes
  823. (si l'OS hôte ne les posseident pas...).
  824.  
  825. Fonctions d'entrées sorties
  826.  
  827. printf(), sprintf(), scanf(), putchar(), puts(), gets(), getchar(),
  828. flushstdout(),
  829.  
  830.  
  831. Fonctions de types
  832.  
  833. isdigit(), islower(), isspace(), ispunct(), isupper(), isalpha(), isxdigit(),
  834. isalnum(), isprint(), isgraph(), iscntrl(), isascii(), isiso(), toupper(),
  835. tolower(), toiso(),
  836.  
  837.  
  838. Fonctions de chaînes
  839.  
  840. strcat(), strchr(), strcmp(), strcpy(), strerror(), strlen(), strncat(),
  841. strncmp(), strncpy(), strrchr(), strdup(), stricmp(), strnicmp(), strcasecmp(),
  842. strncasecmp(), strpbrk(), strstr(), strcoll(), strcspn(), strspn(), strtok(),
  843. strtol(),
  844.  
  845.  
  846. Fonctions de gestion de la mémoire
  847.  
  848. realloc(), malloc(), free(), swab(), memchr(), memcmp(), memcpy(), memmove(),
  849. memset(), memccpy(),
  850.  
  851.  
  852. Autres fonctions
  853.  
  854. exit(), atoi(), time(), ctime(), clock(), sleep(), system(),
  855.